package org.pizza.schemaExt;

import cn.hutool.core.util.ReflectUtil;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import lombok.extern.slf4j.Slf4j;
import org.pizza.annotation.ApiModelEnumProperty;
import org.pizza.annotation.SwaggerDisplayEnum;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
import springfox.documentation.swagger.common.SwaggerPluginSupport;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Collectors;


/**
 * 参数枚举注释文档扩展
 * @author: fyh
 * @date: 2023/1/10
 */
@Slf4j
@Order(SwaggerPluginSupport.OAS_PLUGIN_ORDER)
public class EnumModelPropertyBuilderPlugin implements ModelPropertyBuilderPlugin  {


    @Override
    public void apply(ModelPropertyContext context) {
        try {

            if (!context.getBeanPropertyDefinition().isPresent()) {
                return;
            }

            BeanPropertyDefinition beanPropertyDefinition = context.getBeanPropertyDefinition().get();
            AnnotatedField beanPropertyDefinitionField = beanPropertyDefinition.getField();
            if (beanPropertyDefinitionField == null) {
                return;
            }
            // 没有@ApiModelPropertyEnum， 直接返回
            ApiModelEnumProperty apiModelPropertyEnum =
                    beanPropertyDefinitionField.getAnnotation(ApiModelEnumProperty.class);
            if (apiModelPropertyEnum == null) {
                return;
            }

            // 生成需要拼接的取值含义描述内容
            String valueDesc = generateValueDesc(apiModelPropertyEnum);

            context.getSpecificationBuilder()
                    .description(valueDesc)
                    .required(apiModelPropertyEnum.required())
                    .example(apiModelPropertyEnum.example());
            context.getBuilder()
                    .description(valueDesc)
                    .required(apiModelPropertyEnum.required())
                    .example(apiModelPropertyEnum.example());
        } catch (Exception e) {
            log.error("failed to rebuild swagger param description", e);
        }
    }

    private String generateValueDesc(ApiModelEnumProperty apiModelPropertyEnum)  {
        Class<? extends Enum> rawPrimaryType = apiModelPropertyEnum.referenceClazz();
        SwaggerDisplayEnum swaggerDisplayEnum = AnnotationUtils.findAnnotation(rawPrimaryType,
                SwaggerDisplayEnum.class);
        Class<?> clazz = apiModelPropertyEnum.referenceClazz();
        Object[] enumConstants = clazz.getEnumConstants();
        String enumFullDesc = Arrays.stream(enumConstants)
                .filter(Objects::nonNull)
                .map(enumConsts -> {
                    Method codeMethod = ReflectUtil.getMethod(clazz,swaggerDisplayEnum == null ? "code" : swaggerDisplayEnum.value());
                    Method messageMethod = ReflectUtil.getMethod(clazz,swaggerDisplayEnum == null ? "message" : swaggerDisplayEnum.desc());
                    return ReflectUtil.invoke(enumConsts,codeMethod) + "：" + ReflectUtil.invoke(enumConsts,messageMethod);
                }).collect(Collectors.joining("；"));
        return apiModelPropertyEnum.value() + "（" + enumFullDesc + "）";

    }



    @Override
    public boolean supports(DocumentationType documentationType) {
        return SwaggerPluginSupport.pluginDoesApply(documentationType);
    }
}
